package com.lqk.netty.groupchat.server;

import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;

public class GroupChatServerHandler extends SimpleChannelInboundHandler<String> {

    /**
     * 私聊
     * 使用一个 hashmap 管理 channel
     * 为了实现私聊功能，这里key存储用户的唯一标识，
     * 我保存 客户端的端口号
     * 当然 这个集合也需要自己去维护 用户的上下线 不能像 ChannelGroup那样自己去维护
     */
    public static Map<String,Channel> channelMap = new HashMap<String,Channel>();

    // 定义一个 channel 组，管理所有的 channel
    // GlobalEventExecutor.INSTANCE 是一个全局的事件执行器，是一个单例
    private static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    // handlerAdded 表示连接建立，一旦连接，第一个被执行
    // 将当前 channel 加入到 channelGroup
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        // 拿到当前的 channel
        Channel channel = ctx.channel();

        // 将该客户加入聊天的信息推送给其他在线的客户端
        /*
            channelGroup.writeAndFlush : 该方法会将 channelGroup 中的所有 channel 遍历，并发送消息
            我们不需要自己遍历
         */
        channelGroup.writeAndFlush(df.format(LocalDateTime.now())+"[客户端]: " + channel.remoteAddress() + " 加入聊天\n");
        channelGroup.add(channel);

        // 将所有的客户端端口号存入 channelMap 中
        String key = channel.remoteAddress().toString().split(":")[1];
        channelMap.put(key, channel);
    }

    // handlerRemoved 表示 断开连接, 将 xx 客户离线信息推送给当前在线的客户端，同时当前 channel 也会自动从 ChannelGroup 中被移除
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        channelGroup.writeAndFlush(df.format(LocalDateTime.now())+"[客户端]" + channel.remoteAddress() + "离开了\n");

        //下线移除
        String key = ctx.channel().remoteAddress().toString().split(":")[1];
        channelMap.remove(key);

        System.out.println("channelGroup size: " + channelGroup.size());

    }

    // channelActive 表示 channel 处于活动的状态，可以再次提示 xx 上线了
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("["+df.format(LocalDateTime.now())+"] "+ctx.channel().remoteAddress() + " 上线了~");
    }

    // channelInactive 表示 channel 处于不活动状态， 提示 xx 下线了
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {

        //下线移除
//        String key = ctx.channel().remoteAddress().toString().split(":")[1];
//        channelMap.remove(key);

        System.out.println("["+df.format(LocalDateTime.now())+"] "+ctx.channel().remoteAddress() + " 离线了~");
    }

    // 读取数据
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {

        // 获取到当前 channel
        Channel channel = ctx.channel();
        /**
         * 这里简单判断 如果内容里边包含#那么就是私聊
         */
        if(msg.contains("#")) {
            String id = msg.split("#")[1];
            String body = msg.split("#")[2];
            Channel userChannel = channelMap.get(id);
            userChannel.writeAndFlush("["+df.format(LocalDateTime.now())+"] " + channel.remoteAddress() +  body + "\n");
        }else {
            // 这是我们遍历 channelGroup，根据不同的情况，推送不同的消息
            // 使用流的方式进行处理，直接过滤掉自己进行发送
            channelGroup.stream().filter(ch -> channel != ch).forEach(ch -> ch.writeAndFlush("["+df.format(LocalDateTime.now())+"] "+ channel.remoteAddress() + msg + "\n"));
            // 使用 channelGroup 自带的 forEach，在内部进行过滤处理
//        channelGroup.forEach(ch->{
//            if (channel != ch){ // 不是当前的 channel，直接转发消息
//                ch.writeAndFlush("[用户]" + channel.remoteAddress() + "发送消息" + msg + "\n");
//            }else { // 回显自己发送的消息
//                ch.writeAndFlush("[我]发送了消息" + msg + "\n");
//            }
//        });
        }
    }

    // 发生异常的处理方法
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        // 关闭通道
//        ctx.channel().close();
        ctx.close();
    }
}
